package ltd.zouzheng.myutils.sftp;

import com.jcraft.jsch.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.InputStream;
import java.util.*;

/**
 * 封装jsch的sftp操作
 *
 * @author zouzheng
 * @date 2021/4/9
 */
public class SFTPClient {
    private static final Logger log = LoggerFactory.getLogger(SFTPClient.class);

    public static class Channel implements Closeable {

        private ChannelSftp channelSftp;
        private com.jcraft.jsch.Channel channel;
        private Session session;

        public Channel(ChannelSftp channelSftp, com.jcraft.jsch.Channel channel, Session session) {
            this.channelSftp = channelSftp;
            this.channel = channel;
            this.session = session;
        }

        public void put(String absolutePath, String fileName, String content) throws SftpException {
            put(new FileEntity(absolutePath, fileName, new ByteArrayInputStream(content.getBytes())));
        }

        public void put(String absolutePath, String fileName, InputStream content) throws SftpException {
            put(new FileEntity(absolutePath, fileName, content));
        }

        public void put(FileEntity file) throws SftpException {
            if (!isDirExist(file.getAbsolutePath()) && !createDir(file.getAbsolutePath())) {
                throw new RuntimeException("sftp创建目录失败");
            }
            //进入目录并上传
            channelSftp.cd(file.getAbsolutePath());
            channelSftp.put(file.getContent(), file.getFileName() + ".tmp");
            channelSftp.rename(file.getFileName() + ".tmp", file.getFileName());
        }

        public InputStream get(String absoluteFileName) throws SftpException {
            return channelSftp.get(absoluteFileName);
        }

        public List<String> ls(String absolutePath) throws SftpException {
            List<String> list = new LinkedList<>();
            Vector<ChannelSftp.LsEntry> ls = channelSftp.ls(absolutePath);
            Iterator<ChannelSftp.LsEntry> iterator = ls.iterator();
            while (iterator.hasNext()) {
                ChannelSftp.LsEntry next = iterator.next();
                if (".".equals(next.getFilename()) || "..".equals(next.getFilename())) {
                    continue;
                }
                list.add(next.getFilename());
            }
            return list;
        }

        //创建目录
        public boolean createDir(String directory) {
            try {
                String pathArry[] = directory.split("/");
                StringBuilder dir = new StringBuilder("/");
                for (String path : pathArry) {
                    if ("".equals(path)) {
                        continue;
                    }
                    dir.append(path + "/");
                    if (!isDirExist(dir.toString())) {
                        channelSftp.mkdir(dir.toString());
                    }
                }
                return true;
            } catch (SftpException e) {
                return false;
            }
        }

        //判断目录是否存在
        public boolean isDirExist(String directory) {
            try {
                SftpATTRS sftpATTRS = channelSftp.lstat(directory);
                return sftpATTRS.isDir();
            } catch (Exception e) {
                return false;
            }
        }

        public void rm(String absoluteFileName) throws SftpException {
            channelSftp.rm(absoluteFileName);
        }

        @Override
        public void close() {
            log.debug("sftp断开连接：{}", this.hashCode());
            if (null != channelSftp) {
                channelSftp.disconnect();
            }
            if (null != channel) {
                channel.disconnect();
            }
            if (null != session) {
                session.disconnect();
            }
        }
    }

    public static Channel connect(String host, Integer port, String username, String password) {
        log.debug("sftp连接：host[{}:{}] user[{}] pwd[{}]", host, port, username, password);
        try {
            JSch jSch = new JSch();
            Session session = jSch.getSession(username, host, port);
            session.setPassword(password);
            Properties properties = new Properties();
            properties.put("StrictHostKeyChecking", "no");
            session.setConfig(properties);
            session.connect(1000 * 30);
            com.jcraft.jsch.Channel channel = session.openChannel("sftp");
            channel.connect(60 * 2000);
            ChannelSftp sftp = (ChannelSftp) channel;
            Channel channel1 = new Channel(sftp, channel, session);
            log.debug("sftp连接成功：{}", channel1.hashCode());
            return channel1;
        } catch (JSchException e) {
            log.error("连接SFTP服务器失败：{}", e.getMessage(), e);
            return null;
        }
    }

    public static class FileEntity {
        private String absolutePath; //目标目录绝对路径
        private String fileName;     //目标文件名
        private InputStream content;

        public FileEntity(String absolutePath, String fileName, InputStream content) {
            this.absolutePath = absolutePath;
            this.fileName = fileName;
            this.content = content;
        }

        public String getAbsolutePath() {
            return absolutePath;
        }

        public FileEntity setAbsolutePath(String absolutePath) {
            this.absolutePath = absolutePath;
            return this;
        }

        public String getFileName() {
            return fileName;
        }

        public FileEntity setFileName(String fileName) {
            this.fileName = fileName;
            return this;
        }

        public InputStream getContent() {
            return content;
        }

        public FileEntity setContent(InputStream content) {
            this.content = content;
            return this;
        }
    }
}
